/*
 * Student Name: Philippe Gambling
 * Course: CSCI 5733
 * Assignment: Homework 1
 * Created: Jun 15, 2009
 * Modified: Jun 15, 2009
 * 
 * Class Name: NewsRetriever
 * Description: Gathers news data for a specific topic from the provided sources.
 */
package csci5733.hw1;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

// nu.xom is an open-source XML API.
// Source URL: http://www.xom.nu/
import nu.xom.Attribute;
import nu.xom.Document;
import nu.xom.Element;

/**
 *
 * @author Philippe Gambling
 */
public class NewsRetriever {

    private boolean hasNews = false;
    private List newsSources = new ArrayList();
    private Set newsItemSet = new TreeSet();
    private String xml = "";
    private String xmlToCache = "";

    public NewsRetriever() {
    }

    public void addNewsSource(NewsSource ns) {
        this.newsSources.add(ns);
    }

    /**
     * @return a Boolean true if news for set topic was retrieved, otherwise false.
     */
    public boolean hasNews() {
        return this.hasNews;
    }

    /**
     * @return A String with the gathered news items in XML form.
     */
    public String getXML() {
        return this.xml;
    }

    /**
     * Builds the XML document from merged list of gathered news items.
     */
    private void buildXMLDoc() {
        Element root = new Element("news");
        Attribute copy = new Attribute("copy", "from original sources");
        root.addAttribute(copy);
        Document newsDoc = new Document(root);

        Iterator<NewsItem> itr = newsItemSet.iterator();
        while (itr.hasNext()) {
            addItemToRoot(itr.next(), root);
        }
        newLine(root);
        newLine(root);

        this.xml = newsDoc.toXML();
        copy.setValue("from cached sources");
        this.xmlToCache = newsDoc.toXML();
    }

    private void addItemToRoot(NewsItem ni, Element root) {
        newLine(root);
        intent(root);
        Element newItem = new Element("item");
        Attribute from = new Attribute("from", ni.getSource());
        newItem.addAttribute(from);
        addElemToItem("title", ni.getTitle(), newItem);
        addElemToItem("time", ni.getTimeStr(), newItem);
        addElemToItem("url", ni.getUrl(), newItem);
        newLine(newItem);
        intent(newItem);
        root.appendChild(newItem);
    }

    private void addElemToItem(String name, String text, Element newItem) {
        newLine(newItem);
        intent(newItem);
        intent(newItem);
        Element elem = new Element(name);
        elem.appendChild(text);
        newItem.appendChild(elem);
    }

    private boolean getCachedSources(String topic) {
        this.xml = Cache.getData(topic);
        return (this.xml != null);
        
    }

    private void intent(Element elem) {
        elem.appendChild("  ");
    }

    private void newLine(Element elem) {
        elem.appendChild("\n");
    }

    /**
     * Drives the main flow of events for actually retrieving the news.
     */
    public void fetchNews(String topic) throws Exception {
        topic = topic.toLowerCase();
        if(getCachedSources(topic)) {
            this.hasNews = true;
            return;
        } // Do nothing if cached xml was found
        else { // If not cached, retrieve original sources
            getLatestSources(topic);
            if(this.hasNews) {
                buildXMLDoc();
                updateCache(topic);
            }
        }
    }

    /**
     * Gets latest version of news from known sources
     */
    private void getLatestSources(String topic) throws Exception {
        Iterator<NewsSource> itr = this.newsSources.iterator();
        while (itr.hasNext()) {
            NewsSource newsSource = itr.next();
            List items = newsSource.fetchTopic(topic);
            this.newsItemSet.addAll(items);
        }

        this.hasNews = !this.newsItemSet.isEmpty();
    }

    private void updateCache(String topic) {
        Cache.storeData(topic, this.xmlToCache);
    }
}
